/*
 * @Description  : Common definitions include pin assigns
 * @Version      : 0.1
 * @Company      : V-Think Development Team
 * @Author       : KiraVerSace@yeah.net
 * @Date         : 2020-07-09 14:44:50
 * @LastEditTime : 2023-07-18 17:08:06
 */
#include <xCommon.h>

volatile int32_t  xBuzzer = 0;

void rebootSystem(void)
{
    delayWithLED(1500);
	__disable_irq();
	NVIC_SystemReset();
}

void delayWithLED(uint32_t ms)
{
    if (ms >= 500)
    {
        for (uint32_t i=0; i< (ms/500); i++)
        {
            delay(500);
            digitalToggle(LED);
            digitalToggle(xBuzzer);

            IWatchdog.reload();
        }
        digitalWrite(xBuzzer, LOW);
    }
    else
    {
        delay(ms);
        digitalToggle(LED);

        IWatchdog.reload();
    }
}

void printLogo(void)
{
    Serial.print("\033[2J\033[1H");
    Serial.print("\r\n----------------------------------------------------------------------");
    Serial.printf("\r\n|    (c) 2020 VThink Development Team          [%s]           |", PRODUCT_MODEL);
    Serial.printf("\r\n|          Remote Terminal Unit(RTU) BootLoader V%d.%d.%d               |", SOFTWARE_VERSION_MAJOR, SOFTWARE_VERSION_MINOR, SOFTWARE_VERSION_PATCH);
    Serial.print("\r\n|                                        By   KiraVerSace@yeah.net   |");
    Serial.print("\r\n----------------------------------------------------------------------");
    Serial.print("\r\n\r\n");
}

/*----------------- xShell/xLog -----------------*/
// RTTStream rttStream;
Log xLog;

void xLogWrite(char *buffer, int16_t length)
{
    Serial.write(buffer, length);
    // rttStream.write(buffer, len);
}

void xLogInit(void)
{
    xLog.write = xLogWrite;
    xLog.active = true;
    /*
     * LOG_NONE   	// 无级别
     * LOG_ERROR  	// 错误
     * LOG_WARNING,	// 警告
     * LOG_INFO  	// 消息
     * LOG_DEBUG  	// 调试
     * LOG_VERBOSE	// 冗余
     * LOG_ALL    	// 所有日志
     * 设置为 NONE 则没有任何日志输出
     */
    xLog.level = LOG_ALL;

    logRegister(&xLog);
    logSetLevel(&xLog, LOG_VERBOSE);

    /* 清空控制台 [很奇怪,把这个函数放进logRegister就会执行失败,也许是函数执行完才会完成注册] */
    // xLogWrite((char *)"\033[2J\033[1H", strlen("\033[2J\033[1H"));
}

/*----------------- FRAM ----------------*/
SPIClass          framSPI(FRAM_MOSI, FRAM_MISO, FRAM_SCK);
Adafruit_FRAM_SPI fram(FRAM_CS, &framSPI);

void xCommonInit(void)
{
    /* Set Interrupt Group Priority */
    HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);

    pinMode(LED, OUTPUT);
    digitalWrite(LED, HIGH);

    /* Shell */
    Serial.begin(115200);
    printLogo();

    xLogInit();

    if (IWatchdog.isReset(true))
    {
        logWarning("Reset by the IWatchdog!");
    }
    __HAL_RCC_CLEAR_RESET_FLAGS();
    IWatchdog.begin(18000000);		// 125µs - 32760000µs

    /* FRAM*/
    if (!fram.begin())
	{
		logError("No SPI FRAM found......");
		while (1);
	}
	uint8_t  framManufacturerID = 0;
	uint16_t framProductID = 0;
	fram.getDeviceID(&framManufacturerID, &framProductID);
	if ((framManufacturerID == 0x04)&&(framProductID == 0x2703))	// https://www.fujitsu.com/uk/Images/MB85RS1MT.pdf
	{
		logInfo("FRAM check ...........[PASS]");
	}
	else
	{
		logError("FRAM check ..........[NO]");
	}
}

void xScreenPeriphInit(void)
{
    /* LTE-Cat1-GM5 */
    lte.begin(LTE_UART, LTE_PWR);

#ifdef ULTIMATE
    tft.init();
    tft.fillScreen(TFT_ST7789::COLOR_GRAYBLUE);
    tft.backlightOn();
#endif

#ifdef SIMPLE
    tft.init();
    tft.fillScreen(TFT_ST7735::COLOR_GRAYBLUE);
    tft.backlightOn();
#endif
}

bool loadReadOnlyArea(void)
{
	/* 读取只读区域的参数,并且重新计算校验与所读取的校验值比较,以此判断参数读取是否正确 */
	fram.read(FRAM_AREA_READ_ONLY_START, (uint8_t *)&xReadOnlyArea, sizeof(XReadOnlyAreaT));

    return true;

    /* 暂时取消,因为fastCRC16占用较大空间 */
    if ((fastCRC16.ccitt((uint8_t *)&xReadOnlyArea, sizeof(XReadOnlyAreaT) - 2) == xReadOnlyArea.crc16)
            && (xReadOnlyArea.crc16 != 0))
	{
        logHexDump(&xReadOnlyArea, sizeof(XReadOnlyAreaT));

        logInfo("Load RO-Area successfully!")
        return true;
	}
    else
    {
        logError("Load RO-Area failed, CRC16 error or the area was unconfigured!")
        return false;
    }
}

bool setOTABootLoaderVersion(void)
{
    uint8_t versionTemp[2]       = {0x00, 0x00};
    uint8_t versionTempVerify[2] = {0x00, 0x00};

    versionTemp[0] = highByte(SOFTWARE_VERSION);
    versionTemp[1] = lowByte(SOFTWARE_VERSION);

    fram.writeEnable(true);
    fram.write(FRAM_OTA_BL_VER, versionTemp, 2);
    fram.writeEnable(false);

    fram.read (FRAM_OTA_BL_VER, versionTempVerify, 2);

    if (makeWord(versionTempVerify[0], versionTempVerify[1]) == SOFTWARE_VERSION)
    {
        return true;
    }
    else
    {
        logError("Set the BootLoader version failed!");
        return false;
    }
}

uint8_t readOTAJumpToApplicationTryCount(void)
{
    uint8_t tryCount = 0;

    fram.read(FRAM_OTA_JUMP_TRY_COUNT, &tryCount, 1);

    if ((tryCount == 0x22) || (tryCount == 0x33))
    {
        clearOTAJumpToApplicationTryCount();

        return 0;
    }
    else
    {
        return tryCount;
    }
}

bool setOTAJumpToApplicationTryCount(void)
{
    uint8_t tryCount = readOTAJumpToApplicationTryCount();

    tryCount = tryCount+1;
    if (tryCount >= (UINT8_MAX -1))
    {
        tryCount = 0;
    }

    fram.writeEnable(true);
    fram.write(FRAM_OTA_JUMP_TRY_COUNT, &tryCount, 1);
    fram.writeEnable(false);

    uint8_t tryCountVerify = 0;
    fram.read (FRAM_OTA_JUMP_TRY_COUNT, &tryCountVerify, 1);

    if(tryCountVerify == tryCount)
    {
        return true;
    }
    else
    {
        return false;
    }
}

bool clearOTAJumpToApplicationTryCount(void)
{
    uint8_t tryCount = 0;

    fram.writeEnable(true);
    fram.write(FRAM_OTA_JUMP_TRY_COUNT, &tryCount, 1);
    fram.writeEnable(false);

    uint8_t tryCountVerify = 0;
    fram.read (FRAM_OTA_JUMP_TRY_COUNT, &tryCountVerify, 1);

    if(tryCountVerify == tryCount)
    {
        return true;
    }
    else
    {
        return false;
    }
}

uint8_t readOTAUpgradeFlag(void)
{
    uint16_t otaFlagValue = 0;
    uint8_t otaCode[2] = {0x00, 0x00};

    fram.read(FRAM_OTA_UPGRADE_ADDR_START, otaCode, 2);

    // logDebug("[0x%02X]-[0x%02X]", otaCode[0], otaCode[1]);

    if((otaCode[0] == OTA_DEFAULT_CODE) && (otaCode[1] == OTA_DEFAULT_CODE))
    {
        return OTA_FLAG_CLEAR;
    }
    else if((otaCode[0] == OTA_UPGRADE_ORIGIN_CODE) && (otaCode[1] == OTA_UPGRADE_ORIGIN_CODE))
    {
        return OTA_FLAG_FROM_DEFAULT;
    }
    else if((otaCode[0] == OTA_UPGRADE_APP_CODE) && (otaCode[1] == OTA_UPGRADE_APP_CODE))
    {
        return OTA_FLAG_FROM_APP;
    }
    else
    {
        return OTA_FLAG_CLEAR;
    }
}

bool setOTAUpgradeFlag(enum OTAUpgradeFlag upgradeFlag)
{
    uint8_t otaCode[2]       = {0x00, 0x00};
    uint8_t otaCodeVerify[2] = {0x00, 0x00};

    if(upgradeFlag == OTA_FLAG_CLEAR)
    {
        // logDebug("Clear the OTA flag.");

        otaCode[0] = OTA_DEFAULT_CODE;
        otaCode[1] = OTA_DEFAULT_CODE;
    }
    else if(upgradeFlag == OTA_FLAG_FROM_DEFAULT)
    {
        // logDebug("Set the OTA from default url.");

        otaCode[0] = OTA_UPGRADE_ORIGIN_CODE;
        otaCode[1] = OTA_UPGRADE_ORIGIN_CODE;
    }
    else if(upgradeFlag == OTA_FLAG_FROM_APP)
    {
        // logDebug("Set the OTA from APP Url.");

        otaCode[0] = OTA_UPGRADE_APP_CODE;
        otaCode[1] = OTA_UPGRADE_APP_CODE;
    }
    else
    {
        otaCode[0] = OTA_DEFAULT_CODE;
        otaCode[1] = OTA_DEFAULT_CODE;
    }

    fram.writeEnable(true);
    fram.write(FRAM_OTA_UPGRADE_ADDR_START, otaCode, 2);
    fram.writeEnable(false);

    fram.read (FRAM_OTA_UPGRADE_ADDR_START, otaCodeVerify, 2);

    if ((otaCodeVerify[0] == otaCode[0]) && (otaCodeVerify[1] == otaCode[1]))
    {
        return true;
    }
    else
    {
        logError("Set the OTA upgrade code failed!");
        return false;
    }
}

uint16_t readOTAFirmwareUrl(uint8_t *url)
{
	// logDebug("Read the OTA upgrade firmware's url.");

	uint8_t urlOriginal[512];
	memset(urlOriginal, 0x00, sizeof(urlOriginal));

	fram.read(FRAM_OTA_UPGRADE_ADDR_START, urlOriginal, sizeof(urlOriginal));

    memcpy(url, urlOriginal, sizeof(urlOriginal));

    return sizeof(urlOriginal);
}

bool setOTAFirmwareCRCValue(const uint32_t crcValue)
{
    uint8_t crcSetValue[4];

    crcSetValue[0] = (crcValue>>24)&0xFF;
    crcSetValue[1] = (crcValue>>16)&0xFF;
    crcSetValue[2] = (crcValue>>8)&0xFF;
    crcSetValue[3] = (crcValue)&0xFF;

    fram.writeEnable(true);
    fram.write(FRAM_OTA_FIRMWARE_CRC_START, crcSetValue, 4);
    fram.writeEnable(false);

    uint8_t crcReadValue[4];
    memset(crcReadValue, 0x00, 4);
    fram.read(FRAM_OTA_FIRMWARE_CRC_START, crcReadValue, 4);

    uint32_t crcVerifyValue = ((crcReadValue[0]<<24)+(crcReadValue[1]<<16)+(crcReadValue[2]<<8)+crcReadValue[3]);

    if (crcVerifyValue == crcValue)
    {
        return true;
    }
    else
    {
        logError("Set the OTA CRC32 failed!");
        return false;
    }
}

uint32_t readOTAFirmwareCRCValue(void)
{
    uint8_t crcValue[4];

    fram.read(FRAM_OTA_FIRMWARE_CRC_START, crcValue, 4);

    return ((crcValue[0]<<24)+(crcValue[1]<<16)+(crcValue[2]<<8)+crcValue[3]);
}

bool setOTANotifyFlag(void)
{
	uint8_t otaCode[2]       = {OTA_NOTIFY_CODE, OTA_NOTIFY_CODE};
	uint8_t otaCodeVerify[2] = {0x00, 0x00};

    fram.writeEnable(true);
    fram.write(FRAM_OTA_NOTIFY_ADDR_START, otaCode,       2);
    fram.writeEnable(false);
    fram.read (FRAM_OTA_NOTIFY_ADDR_START, otaCodeVerify, 2);

	if((otaCodeVerify[0] == OTA_NOTIFY_CODE) && (otaCodeVerify[1] == OTA_NOTIFY_CODE))
	{
		return true;
	}
	else
	{
		logError("Clear the OTA notify flag failed!");
		return false;
	}
}

uint8_t readOTAUpgradeFromApplicationTryCount(void)
{
    uint8_t tryCount = 0;

    fram.read(FRAM_OTA_UPGRADE_TRY_COUNT, &tryCount, 1);

    return tryCount;
}

bool setOTAUpgradeFromApplicationTryCount(bool status)    // true=次数+1 false=次数清零
{
    uint8_t tryCount = readOTAUpgradeFromApplicationTryCount();

    if(status == false)
    {
        tryCount = 0;
    }
    else
    {
        tryCount = tryCount+1;
        if (tryCount >= (UINT8_MAX -1))
        {
            tryCount = 0;
        }
    }

    fram.writeEnable(true);
    fram.write(FRAM_OTA_UPGRADE_TRY_COUNT, &tryCount, 1);
    fram.writeEnable(false);

    uint8_t tryCountVerify = 0;
    fram.read (FRAM_OTA_UPGRADE_TRY_COUNT, &tryCountVerify, 1);

    if(tryCountVerify == tryCount)
    {
        return true;
    }
    else
    {
        return false;
    }
}
